### Daytime.6 - An asynchronous UDP daytime server

##### The main() function

    int main()
    {
      try
      {

Create a server object to accept incoming client requests, and run the io_context object.

        asio::io_context io_context;
        udp_server server(io_context);
        io_context.run();
      }
      catch (std::exception& e)
      {
        std::cerr << e.what() << std::endl;
      }

      return 0;
    }

##### The udp_server class

    class udp_server
    {
    public:

The constructor initialises a socket to listen on UDP port 13.

      udp_server(asio::io_context& io_context)
        : socket_(io_context, udp::endpoint(udp::v4(), 13))
      {
        start_receive();
      }

    private:
      void start_receive()
      {

The function ip::udp::socket::async_receive_from() will cause the application to listen in the background for a new request. When such a request is received, the io_context object will invoke the `handle_receive()` function with two arguments: a value of type std::error_code indicating whether the operation succeeded or failed, and a `size_t` value `bytes_transferred` specifying the number of bytes received.

        socket_.async_receive_from(
            asio::buffer(recv_buffer_), remote_endpoint_,
            std::bind(&udp_server::handle_receive, this,
              asio::placeholders::error,
              asio::placeholders::bytes_transferred));
      }

The function `handle_receive()` will service the client request.

      void handle_receive(const std::error_code& error,
          std::size_t /*bytes_transferred*/)
      {

The `error` parameter contains the result of the asynchronous operation. Since we only provide the 1-byte `recv_buffer_` to contain the client's request, the io_context object would return an error if the client sent anything larger. We can ignore such an error if it comes up.

        if (!error)
        {

Determine what we are going to send.

          std::shared_ptr<std::string> message(
              new std::string(make_daytime_string()));

We now call ip::udp::socket::async_send_to() to serve the data to the client.

          socket_.async_send_to(asio::buffer(*message), remote_endpoint_,
              std::bind(&udp_server::handle_send, this, message,
                asio::placeholders::error,
                asio::placeholders::bytes_transferred));

When initiating the asynchronous operation, and if using `std::bind`, you must specify only the arguments that match the handler's parameter list. In this program, both of the argument placeholders (asio::placeholders::error and asio::placeholders::bytes_transferred) could potentially have been removed.

Start listening for the next client request.

          start_receive();

Any further actions for this client request are now the responsibility of `handle_send()`.

        }
      }

The function `handle_send()` is invoked after the service request has been completed.

      void handle_send(std::shared_ptr<std::string> /*message*/,
          const std::error_code& /*error*/,
          std::size_t /*bytes_transferred*/)
      {
      }

      udp::socket socket_;
      udp::endpoint remote_endpoint_;
      std::array<char, 1> recv_buffer_;
    };

See the full source listing

Return to the tutorial index

Previous: Daytime.5 - A synchronous UDP daytime server

Next: Daytime.7 - A combined TCP/UDP asynchronous server